    #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <inttypes.h>
#include <termios.h>

#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 8080
#define BUFFER_SIZE 4096
#define MAX_FILENAME_LEN 256
#define MD5_DIGEST_LENGTH 16

// 隐藏输入的回显（用于密码输入）
void disableEcho() {
    struct termios term;
    tcgetattr(STDIN_FILENO, &term);
    term.c_lflag &= ~ECHO;
    tcsetattr(STDIN_FILENO, TCSANOW, &term);
}

// 恢复输入的回显
void enableEcho() {
    struct termios term;
    tcgetattr(STDIN_FILENO, &term);
    term.c_lflag |= ECHO;
    tcsetattr(STDIN_FILENO, TCSANOW, &term);
}

// 连接到服务器
int connect_to_server() {
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
        perror("socket creation failed");
        return -1;
    }
    
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    
    if (inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr) <= 0) {
        perror("invalid address");
        close(sock);
        return -1;
    }
    
    if (connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        perror("connection failed");
        close(sock);
        return -1;
    }
    
    return sock;
}

// 序列化字符串（长度前缀法）
int serialize_string(const char* str, uint8_t* buffer, size_t buffer_size) {
    size_t len = strlen(str);
    if (len + sizeof(uint32_t) > buffer_size) {
        return -1;
    }
    
    uint32_t net_len = htonl((uint32_t)len);
    memcpy(buffer, &net_len, sizeof(net_len));
    memcpy(buffer + sizeof(net_len), str, len);
    
    return sizeof(net_len) + len;
}

// 反序列化字符串
int deserialize_string(const uint8_t* buffer, char* str, size_t str_size) {
    uint32_t net_len;
    memcpy(&net_len, buffer, sizeof(net_len));
    size_t len = ntohl(net_len);
    
    if (len >= str_size) {
        return -1;
    }
    
    memcpy(str, buffer + sizeof(net_len), len);
    str[len] = '\0';
    
    return sizeof(net_len) + len;
}

// 从用户获取输入
int get_input(const char* prompt, char* buffer, size_t buffer_size, int is_password) {
    printf("%s", prompt);
    fflush(stdout);
    
    if (is_password) {
        disableEcho();
    }
    
    if (fgets(buffer, buffer_size, stdin) == NULL) {
        if (is_password) {
            enableEcho();
        }
        return -1;
    }
    
    // 移除换行符
    buffer[strcspn(buffer, "\n")] = '\0';
    
    if (is_password) {
        enableEcho();
        printf("\n");
    }
    
    return 0;
}

// 发送登录请求
int send_login_request(int sock, const char* username, const char* password) {
    uint8_t buffer[BUFFER_SIZE];
    int offset = 0;
    
    // 序列化用户名
    int username_len = serialize_string(username, buffer + offset, BUFFER_SIZE - offset);
    if (username_len < 0) {
        printf("用户名太长\n");
        return -1;
    }
    offset += username_len;
    
    // 序列化密码
    int password_len = serialize_string(password, buffer + offset, BUFFER_SIZE - offset);
    if (password_len < 0) {
        printf("密码太长\n");
        return -1;
    }
    offset += password_len;
    
    // 发送数据
    if (send(sock, buffer, offset, 0) < 0) {
        perror("发送失败");
        return -1;
    }
    
    return 0;
}

// 接收登录响应
int receive_login_response(int sock) {
    uint8_t response;
    int bytes_received = recv(sock, &response, 1, 0);
    
    if (bytes_received <= 0) {
        perror("接收登录响应失败");
        return -1;
    }
    
    return response;
}

// 发送文件信息请求
int send_file_info_request(int sock, const char* filename) {
    uint8_t buffer[BUFFER_SIZE];
    
    // 请求类型：文件信息请求
    buffer[0] = 1;
    
    // 序列化文件名
    int filename_len = serialize_string(filename, buffer + 1, BUFFER_SIZE - 1);
    if (filename_len < 0) {
        printf("文件名太长\n");
        return -1;
    }
    
    // 发送请求
    if (send(sock, buffer, 1 + filename_len, 0) < 0) {
        perror("发送文件信息请求失败");
        return -1;
    }
    
    return 0;
}

// 接收文件信息响应
int receive_file_info_response(int sock, char* filename, size_t filename_size, 
                              size_t* file_size, uint8_t* md5_digest) {
    uint8_t buffer[BUFFER_SIZE];
    int total_received = 0;
    
    // 接收文件名长度
    while (total_received < sizeof(uint32_t)) {
        int received = recv(sock, buffer + total_received, 
                           sizeof(uint32_t) - total_received, 0);
        if (received <= 0) {
            perror("接收文件名长度失败");
            return -1;
        }
        total_received += received;
    }
    
    // 解析文件名长度
    uint32_t net_filename_len;
    memcpy(&net_filename_len, buffer, sizeof(net_filename_len));
    size_t filename_len = ntohl(net_filename_len);
    
    // 接收文件名
    total_received = 0;
    while (total_received < filename_len) {
        int received = recv(sock, buffer + sizeof(uint32_t) + total_received, 
                           filename_len - total_received, 0);
        if (received <= 0) {
            perror("接收文件名失败");
            return -1;
        }
        total_received += received;
    }
    
    // 确保文件名以NULL结尾
    size_t copy_len = filename_len < filename_size - 1 ? filename_len : filename_size - 1;
    memcpy(filename, buffer + sizeof(uint32_t), copy_len);
    filename[copy_len] = '\0';
    
    // 接收文件大小
    total_received = 0;
    while (total_received < sizeof(uint64_t)) {
        int received = recv(sock, (uint8_t*)file_size + total_received, 
                           sizeof(uint64_t) - total_received, 0);
        if (received <= 0) {
            perror("接收文件大小失败");
            return -1;
        }
        total_received += received;
    }
    
    // 转换文件大小字节序
    *file_size = be64toh(*file_size);
    
    // 接收MD5摘要
    total_received = 0;
    while (total_received < MD5_DIGEST_LENGTH) {
        int received = recv(sock, md5_digest + total_received, 
                           MD5_DIGEST_LENGTH - total_received, 0);
        if (received <= 0) {
            perror("接收MD5摘要失败");
            return -1;
        }
        total_received += received;
    }
    
    return 0;
}

// 发送文件块请求
int send_file_chunk_request(int sock, const char* filename, uint64_t offset, uint64_t chunk_size) {
    uint8_t buffer[BUFFER_SIZE];
    int offset_pos = 0;
    
    // 请求类型：文件块请求
    buffer[offset_pos++] = 2;
    
    // 序列化文件名
    int filename_len = serialize_string(filename, buffer + offset_pos, BUFFER_SIZE - offset_pos);
    if (filename_len < 0) {
        printf("文件名太长\n");
        return -1;
    }
    offset_pos += filename_len;
    
    // 序列化偏移量（大端序）
    uint64_t net_offset = htobe64(offset);
    memcpy(buffer + offset_pos, &net_offset, sizeof(net_offset));
    offset_pos += sizeof(net_offset);
    
    // 序列化块大小（大端序）
    uint64_t net_chunk_size = htobe64(chunk_size);
    memcpy(buffer + offset_pos, &net_chunk_size, sizeof(net_chunk_size));
    offset_pos += sizeof(net_chunk_size);
    
    // 发送请求
    if (send(sock, buffer, offset_pos, 0) < 0) {
        perror("发送文件块请求失败");
        return -1;
    }
    
    return 0;
}

// 接收文件块响应
int receive_file_chunk_response(int sock, uint64_t* offset, uint64_t* chunk_size, 
                               uint8_t* data, size_t data_size, size_t* data_received) {
    uint8_t buffer[BUFFER_SIZE];
    int total_received = 0;
    
    // 接收偏移量
    while (total_received < sizeof(uint64_t)) {
        int received = recv(sock, buffer + total_received, 
                           sizeof(uint64_t) - total_received, 0);
        if (received <= 0) {
            perror("接收偏移量失败");
            return -1;
        }
        total_received += received;
    }
    
    // 解析偏移量
    uint64_t net_offset;
    memcpy(&net_offset, buffer, sizeof(net_offset));
    *offset = be64toh(net_offset);
    
    // 接收块大小
    total_received = 0;
    while (total_received < sizeof(uint64_t)) {
        int received = recv(sock, (uint8_t*)chunk_size + total_received, 
                           sizeof(uint64_t) - total_received, 0);
        if (received <= 0) {
            perror("接收块大小失败");
            return -1;
        }
        total_received += received;
    }
    
    // 转换块大小字节序
    *chunk_size = be64toh(*chunk_size);
    
    // 接收数据块
    *data_received = 0;
    while (*data_received < *chunk_size && *data_received < data_size) {
        int to_receive = *chunk_size - *data_received;
        if (to_receive > BUFFER_SIZE) {
            to_receive = BUFFER_SIZE;
        }
        
        int received = recv(sock, data + *data_received, to_receive, 0);
        if (received <= 0) {
            perror("接收数据失败");
            return -1;
        }
        
        *data_received += received;
        
        // 显示进度
        int progress = (int)((double)(*data_received) / (*chunk_size) * 100);
        printf("\r接收进度: %d%%", progress);
        fflush(stdout);
    }
    
    printf("\n");
    return 0;
}

// 发送退出请求
void send_exit_request(int sock) {
    uint8_t request_type = 0; // 退出请求
    send(sock, &request_type, 1, 0);
}

int main() {
    char username[32];
    char password[32];
    char filename[MAX_FILENAME_LEN];
    
    // 获取用户名和密码
    if (get_input("用户名: ", username, sizeof(username), 0) < 0) {
        printf("获取用户名失败\n");
        return 1;
    }
    
    if (get_input("密码: ", password, sizeof(password), 1) < 0) {
        printf("获取密码失败\n");
        return 1;
    }
    
    // 连接到服务器
    int sock = connect_to_server();
    if (sock < 0) {
        return 1;
    }
    
    printf("已连接到服务器\n");
    
    // 发送登录请求
    if (send_login_request(sock, username, password) < 0) {
        close(sock);
        return 1;
    }
    
    // 接收登录响应
    int login_result = receive_login_response(sock);
    if (login_result <= 0) {
        printf("登录失败\n");
        close(sock);
        return 1;
    }
    
    printf("登录成功！\n");
    
    // 获取文件名
    printf("请输入要下载的文件名: ");
    if (fgets(filename, sizeof(filename), stdin) == NULL) {
        printf("获取文件名失败\n");
        close(sock);
        return 1;
    }
    
    // 移除换行符
    filename[strcspn(filename, "\n")] = '\0';
    
    // 发送文件信息请求
    if (send_file_info_request(sock, filename) < 0) {
        close(sock);
        return 1;
    }
    
    // 接收文件信息响应
    char received_filename[MAX_FILENAME_LEN];
    size_t file_size;
    uint8_t md5_digest[MD5_DIGEST_LENGTH];
    
    if (receive_file_info_response(sock, received_filename, sizeof(received_filename), 
                                  &file_size, md5_digest) < 0) {
        close(sock);
        return 1;
    }
    
    if (file_size == 0) {
        printf("文件不存在或无法访问: %s\n", filename);
        close(sock);
        return 1;
    }
    
    printf("文件信息:\n");
    printf("  文件名: %s\n", received_filename);
    printf("  文件大小: %zu bytes\n", file_size);
    printf("  MD5: ");
    for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
        printf("%02x", md5_digest[i]);
    }
    printf("\n");
    
    // 文件块下载循环
    uint64_t chunk_size;
    printf("请输入块大小 (字节): ");
    if (scanf("%" SCNu64, &chunk_size) != 1) {
        printf("输入无效\n");
        close(sock);
        return 1;
    }
    
    // 清空输入缓冲区
    int c;
    while ((c = getchar()) != '\n' && c != EOF);
    
    uint64_t offset = 0;
    int chunk_count = 0;
    
    while (offset < file_size) {
        chunk_count++;
        printf("\n=== 请求块 #%d ===\n", chunk_count);
        printf("偏移量: %" PRIu64 ", 块大小: %" PRIu64 "\n", offset, chunk_size);
        
        // 发送文件块请求
        if (send_file_chunk_request(sock, filename, offset, chunk_size) < 0) {
            printf("发送文件块请求失败\n");
            break;
        }
        
        // 接收文件块响应
        uint64_t received_offset;
        uint64_t received_chunk_size;
        uint8_t data[BUFFER_SIZE * 10];
        size_t data_received;
        
        if (receive_file_chunk_response(sock, &received_offset, &received_chunk_size, 
                                       data, sizeof(data), &data_received) < 0) {
            printf("接收文件块失败\n");
            break;
        }
        
        printf("接收到的块: 偏移量 %" PRIu64 ", 大小 %" PRIu64 "\n", 
               received_offset, received_chunk_size);
        
        // 保存接收到的数据
        char output_filename[256];
        snprintf(output_filename, sizeof(output_filename), "%s.part_%d", filename, chunk_count);
        
        FILE* file = fopen(output_filename, "wb");
        if (file) {
            fwrite(data, 1, data_received, file);
            fclose(file);
            printf("数据已保存到: %s\n", output_filename);
        } else {
            perror("保存文件失败");
        }
        
        // 更新偏移量
        offset += received_chunk_size;
        
        if (received_chunk_size == 0) {
            printf("接收到空块，下载完成或出错\n");
            break;
        }
        
        // 询问是否继续
        if (offset < file_size) {
            printf("按回车继续下载下一个块，或输入 'q' 退出: ");
            char input[10];
            if (fgets(input, sizeof(input), stdin) != NULL) {
                if (input[0] == 'q' || input[0] == 'Q') {
                    break;
                }
            }
        }
    }
    
    printf("\n下载完成。总共下载了 %d 个块。\n", chunk_count);
    
    // 发送退出请求
    send_exit_request(sock);
    
    close(sock);
    return 0;
}